/*
Copyright 2021 LuJun

Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated documentation files (the "Software"), to deal 
in the Software without restriction, including without limitation the rights 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
copies of the Software, and to permit persons to whom the Software is furnished 
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in 
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import { game, Node, director, AudioClip, AudioSource } from 'cc';
import { DEFAULT_BGM_VOLUME, DEFAULT_SE_VOLUME, Macro, request } from '../core';
import { indexMgr } from '../index';

const TAG = 'lcc/audio/audio-manager';

/**
 * @zh
 * 音频配置
 */
export interface AudioConfig {
    /**
     * 文件
     */
    file: string;

    /**
     * 音量
     */
    volume?: number;

    /**
     * 循环
     */
    loop?: boolean;
}

/**
 * @zh
 * 音频数据
 */
interface AudioData {
    /**
     * 剪辑
     */
    clip: AudioClip;

    /**
     * 配置
     */
    config: AudioConfig;

    /**
     * 预加载
     */
    preload?: boolean;
}

/**
 * @zh
 * 音频管理器
 */
export class AudioManager {
    /**
     * @zh
     * 静态对象
     */
    private static _instance: AudioManager = null!;

    /**
     * @zh
     * 获得单例对象
     */
    static get instance() {
        if (AudioManager._instance === null) {
            AudioManager._instance = new AudioManager();
        }
        return AudioManager._instance;
    }

    /**
     * 私有构造
     */
    private constructor() {}

    /**
     * @zh
     * 音频数据
     */
    private _audioData: { [key: string]: AudioData } = Object.create(null);

    /**
     * @zh
     * 音频节点
     */
    private _audioNode: Node = null;

    /**
     * @zh
     * 获得音频节点
     */
    get audioNode() {
        if (this._audioNode === null) {
            this._audioNode = new Node('AudioManager');
            director.getScene()!.addChild(this._audioNode);
            game.addPersistRootNode(this._audioNode);
        }
        return this._audioNode;
    }

    /**
     * @zh
     * BGM源
     */
    private _BGM: AudioSource = null;

    /**
     * @zh
     * 获得BGM源
     */
    private get BGM() {
        if (this._BGM === null) {
            this._BGM = this.audioNode.addComponent(AudioSource);
        }
        return this._BGM;
    }

    /**
     * @zh
     * BGM 数据
     */
    private _BGMData: AudioData = null;

    /**
     * @zh
     * BGM 音量
     */
    private _BGMVolume: number = DEFAULT_BGM_VOLUME;

    /**
     * @zh
     * 获得 BGM 音量
     */
    get BGMVolume() {
        return this._BGMVolume;
    }

    /**
     * @zh
     * 设置 BGM 音量
     */
    set BGMVolume(value: number) {
        this._BGMVolume = Math.min(Math.max(0, value), 1);
        if (this._BGMData) {
            this.BGM.volume = this._BGMVolume * (this._BGMData.config.volume ?? 1);
        }
    }

    /**
     * @zh
     * SE源
     */
    private _SE: AudioSource = null;

    /**
     * @zh
     * 获得SE源
     */
    private get SE() {
        if (this._SE === null) {
            this._SE = this.audioNode.addComponent(AudioSource);
        }
        return this._SE;
    }

    /**
     * @zh
     * SE 数据
     */
    private _SEData: AudioData = null;

    /**
     * @zh
     * SE 音量
     */
    private _SEVolume: number = DEFAULT_SE_VOLUME;

    /**
     * @zh
     * 获得 SE 音量
     */
    get SEVolume() {
        return this._SEVolume;
    }

    /**
     * @zh
     * 设置 SE 音量
     */
    set SEVolume(value: number) {
        this._SEVolume = Math.min(Math.max(0, value), 1);
        if (this._SEData) {
            this.SE.volume = this._SEVolume * (this._SEData.config.volume ?? 1);
        }
    }

    /**
     * @zh
     * 获取所有音频
     */
    getAllAudios() {
        return Object.keys(indexMgr.getIndex(Macro.INDEXPATH_AUDIO));
    }

    /**
     * @zh
     * 释放指定音频
     *
     * @param aname 音频名称
     * @param preload 预加载的也释放
     */
    releaseAudio(aname: string, preload: boolean = false) {
        let audioconf = this._audioData[aname];
        if (audioconf && (!audioconf.preload || preload)) {
            delete this._audioData[aname];
            audioconf.clip.decRef();
        }
    }

    /**
     * @zh
     *
     * 释放音频数组
     *
     * @param anames 音频名称数组
     * @param preload 预加载的也释放
     */
    releaseAudios(anames: string[], preload: boolean = false) {
        for (let aname of anames) {
            this.releaseAudio(aname, preload);
        }
    }

    /**
     * @zh
     * 获得音频数据
     *
     * @param aname 音频名称
     */
    async getAudioData(aname: string) {
        let audiodata = this._audioData[aname];
        if (!audiodata) {
            let config = indexMgr.getIndex(Macro.INDEXPATH_AUDIO + '/' + aname);
            if (config) {
                let clip = await request.get<AudioClip>(config.file, { subproto: 'AudioClip' });
                audiodata = {
                    clip: clip,
                    config: config
                };
                clip.addRef();
                this._audioData[aname] = audiodata;
            }
        }
        return audiodata;
    }
    /**
     * @zh
     * 预加载指定音频
     *
     * @param aname 音频名称
     */
    async preloadAudio(aname: string) {
        (await this.getAudioData(aname)).preload = true;
    }

    /**
     * @zh
     * 预加载音频数组
     *
     * @param anames 音频名称数组
     */
    async preloadAudios(anames: string[], progress = (p: number) => {}) {
        for (let i = 0, len = anames.length; i < len; i++) {
            await this.preloadAudio(anames[i]);
            progress((i + 1) / len);
        }
    }

    /**
     * @zh
     * 停止当前BGN
     */
    stopBGM() {
        this.BGM.stop();
    }

    /**
     * @zh
     * 暂停当前BGN
     */
    pauseBGM() {
        this.BGM.pause();
    }

    /**
     * @zh
     * 播放背景音乐
     *
     * @param aname 可选，音频名称。如果不指定音频名称，则表示播放暂停的音乐
     * @param loop 可选，是否循环
     */
    async playBGM(aname: string = null, loop: boolean = true) {
        if (aname) {
            this._BGMData = await this.getAudioData(aname);
            if (this._BGMData) {
                this.stopBGM();
                this.BGM.clip = this._BGMData.clip;
                this.BGM.loop = loop ?? this._BGMData.config.loop;
                this.BGM.volume = this._BGMVolume * (this._BGMData.config.volume ?? 1);
                this.BGM.currentTime = 0;
                this.BGM.play();
            }
        } else {
            this.BGM.play();
        }
    }

    /**
     * @zh
     * 停止当前SE
     */
    stopSE() {
        this.SE.stop();
    }

    /**
     * @zh
     * 暂停当前SE
     */
    pauseSE() {
        this.SE.pause();
    }

    /**
     * @zh
     * 播放音效
     *
     * @param aname 可选，音频名称。如果不指定音频名称，则表示播放暂停的音频
     */
    async playSE(aname: string = null) {
        if (aname) {
            this._SEData = await this.getAudioData(aname);
            if (this._SEData) {
                this.SE.clip = this._SEData.clip;
                this.SE.loop = false;
                this.SE.volume = this._SEVolume * (this._SEData.config.volume ?? 1);
                this.SE.currentTime = 0;
                this.SE.play();
            }
        } else {
            this.SE.play();
        }
    }
}
